﻿#ifndef SEMIAUTOPLINESFINDER_H
#define SEMIAUTOPLINESFINDER_H

/// 这个代码用到了 plinesfit_opencv.cpp 里的函数。

#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"

/**
 * @brief semiautoPlinesFinder 在一幅图像中半自动的提取出两条平行线，并计算距离
 * @param edge  输入图像，这个图像是已经做边缘提取之后的了。
 * @param start  起点
 * @param end 终点，沿着起点->终点这条直线的方向找边缘，并且是在这条直线的附近范围（离直线距离不超过 BAR_LEN）
 * @param [out] p1  p1->p2 形成的直线与两条平行线垂直，并且 p1 在第一条平行线上，p2 在第二条平行线上。
 * @param [out] p2  p1->p2 的中点过 start->end 形成的直线。
 * @param BAR_LEN 在直线距离不超过 BAR_LEN 的范围内找数据点
 * @return true 表示成功， false 表示失败
 */
double semiautoPlinesFinder(cv::Mat &A, cv::Mat &edge, cv::Point2i start, cv::Point2i end,
                            cv::Point2d &p1, cv::Point2d &p2, int BAR_LEN = 10);

/**
 * @brief cagaus 全选主元高斯消去法求解线性代数方程组 Ax = b
 * @note 这个代码改编自徐世良编著的《C常用算法程序集》
 * @param A[] 线性代数方程组的系数矩阵，要求系数矩阵是 n * n 方阵，行优先存储。
 * @param b[] n 个元素的数组。
 * @param perm[] 辅助数组，n 个元素。函数利用这个数组来记录选主元过程中的位置交换。
 * @param n 线性代数方程组的阶数。
 * @param x[] 计算的结果。
 * @return true 表示计算成功，false 则表明系数矩阵奇异
 */
bool cagaus(double A[],double b[], int perm[], int N, double x[]);

/**
 * @brief crossPoint 计算两条直线的交点
 * @param line_eq1 三个元素的数组，存放 [a, b, c], 这里 a x + b y + c = 0 是第一条直线
 * @param line_eq2 三个元素的数组，存放 [a, b, c], 这里 a x + b y + c = 0 是第二条直线
 * @return 交点坐标，如果失败则返回 Point2d(-DBL_MAX, -DBL_MAX)
 */
cv::Point2d crossPoint(const double line_eq1[3], const double line_eq2[2]);


/**
 * @brief crossPoint 计算两条直线的交点
 * @param a1 第一条直线的系数， 这里 a1 x + b1 y + c1 = 0 是第一条直线
 * @param b1 第一条直线的系数， 这里 a1 x + b1 y + c1 = 0 是第一条直线
 * @param c1 第一条直线的系数， 这里 a1 x + b1 y + c1 = 0 是第一条直线
 * @param a2 第二条直线的系数， 这里 a2 x + b2 y + c2 = 0 是第一条直线
 * @param b2 第二条直线的系数， 这里 a2 x + b2 y + c2 = 0 是第一条直线
 * @param c2 第二条直线的系数， 这里 a2 x + b2 y + c2 = 0 是第一条直线
 * @return 交点坐标，如果失败则返回 Point2d(-DBL_MAX, -DBL_MAX)
 */
cv::Point2d crossPoint(double a1, double b1, double c1, double a2, double b2, double c2);

/**
 * @brief crossPoint 计算两条直线的交点
 * @param p1 第一条直线给出直线上两个点的坐标
 * @param p2 第一条直线给出直线上两个点的坐标
 * @param a 第二条直线的系数， 这里 a2 x + b2 y + c2 = 0 是第一条直线
 * @param b 第二条直线的系数， 这里 a2 x + b2 y + c2 = 0 是第一条直线
 * @param c 第二条直线的系数， 这里 a2 x + b2 y + c2 = 0 是第一条直线
 * @return
 */
cv::Point2d crossPoint(cv::Point2i p1, cv::Point2i p2, double a2, double b2, double c2);

/**
 * @brief findPline 在一幅图像中将两个平行线的坐标点提取出来。
 * @param edge 输入图像，这个图像是已经做边缘提取之后的了。
 * @param start 起点
 * @param end 终点，沿着起点->终点这条直线的方向找边缘，并且是在这条直线的附近范围（离直线距离不超过 BAR_LEN）
 * @param line1 找到的第一条平行线的数据点
 * @param line2 找到的第二条平行线的数据点
 * @param BAR_LEN 在直线距离不超过 BAR_LEN 的范围内找数据点
 */
void findPline(const cv::Mat &edge, cv::Point2i start, cv::Point2i end,
                 std::vector<cv::Point2d> &line1,
                 std::vector<cv::Point2d> &line2, int BAR_LEN = 10);

/**
 * @brief findCrossPoint 从 start 向 end 方向遍历，找到一个值为 255 的点。
 * @param edge 做完阈值处理的图像。在这个图像中沿直线方向寻找第一个 255 点。
 * @param start 直线段的起点。
 * @param end 直线段的终点，在这个范围内寻找值为 255 的点。
 * @return 返回找到的点。如果没找到任何点，则返回 Point2d(-1, -1)  图像上不会有负数坐标的点的。所以可以区分正常点。
 */
cv::Point2d findCrossPoint(const cv::Mat &edge, cv::Point2i start, cv::Point2i end);

/**
 * @brief midPoint 计算两个点的中点
 * @param p1 第一个点
 * @param p2 第二个点
 * @return  中点
 */
cv::Point2d midPoint(cv::Point2d p1, cv::Point2d p2);
#endif // SEMIAUTOPLINESFINDER_H
